﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace Flexible.Implementions.Helpers
{
    public class Converter
    {
        /// <summary>
        /// Convert a value to another providerType
        /// </summary>
        /// <param name="source">the value</param>
        /// <param name="destType">the destination providerType</param>
        /// <returns>converted value or default value(0 or null)</returns>
        /// <exception cref="System.ArgumentNullException">destType is null</exception>
        /// <exception cref="System.InvalidCastException">Can't convert the value to destType.</exception>
        public static object ConvertTo(object source, Type destType)
        {
            if (destType == null)
                throw new ArgumentNullException("destType");

            if (source == null)
                return GetDefaultValue(destType);

            Type sourceType = source.GetType();

            if (destType.IsAssignableFrom(sourceType))
                return source;

            object result = null;
            if (sourceType.GetInterface("System.IConvertible", false) != null)
            {
                try
                {
                    result = Convert.ChangeType(source, destType);
                }
                catch (InvalidCastException)
                {
                }
            }

            if (result == null)
            {
                TypeConverter fromConverter = TypeDescriptor.GetConverter(destType);
                if (fromConverter != null)
                {
                    if (fromConverter.CanConvertFrom(sourceType))
                        result = fromConverter.ConvertFrom(source);
                }
            }

            if (result == null)
            {
                TypeConverter toConverter = TypeDescriptor.GetConverter(sourceType);
                if (toConverter != null)
                {
                    if (toConverter.CanConvertTo(destType))
                        result = toConverter.ConvertTo(source, destType);
                }
            }

            if (result == null)
                throw new InvalidCastException();

            return result;
        }

        private static object GetDefaultValue(Type destType)
        {
            if (destType.IsValueType)
                return destType.GetConstructor(Type.EmptyTypes).Invoke(new object[0]);
            return null;
        }
    }
}
